package org.finesys.common.quartz.config;

import lombok.RequiredArgsConstructor;
import org.finesys.common.quartz.core.facotry.YamlPropertiesSourceFactory;
import org.quartz.Calendar;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;


@AutoConfiguration
@EnableAsync
@ConditionalOnClass({Scheduler.class, SchedulerFactoryBean.class})
@PropertySource(value = "classpath:quartz-config.yml", factory = YamlPropertiesSourceFactory.class)
@EnableConfigurationProperties(QuartzProperties.class)
@RequiredArgsConstructor
public class QuartzAutoConfiguration {

    private final QuartzProperties quartzProperties;
    private final List<SchedulerFactoryBeanCustomizer> factoryBeanCustomizerList;
    private final JobDetail[] jobDetails;
    private final Map<String, Calendar> calendars;
    private final Trigger[] triggers;
    private final ApplicationContext applicationContext;


    @Bean
    @ConditionalOnMissingBean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(new AutowireCapableBeanJobFactory(this.applicationContext.getAutowireCapableBeanFactory()));
        if (!quartzProperties.getProperties().isEmpty()) {
            schedulerFactoryBean.setQuartzProperties(this.asProperties(quartzProperties.getProperties()));
        }
        if (Objects.nonNull(this.jobDetails) && this.jobDetails.length > 0) {
            schedulerFactoryBean.setJobDetails(jobDetails);
        }
        if (!CollectionUtils.isEmpty(calendars)) {
            schedulerFactoryBean.setCalendars(calendars);
        }
        if (Objects.nonNull(this.triggers) && this.triggers.length > 0) {
            schedulerFactoryBean.setTriggers(triggers);
        }
        //使得Quartz作业可以利用Spring的依赖注入和AOP特性‌
        schedulerFactoryBean.setJobFactory(new AutowireCapableBeanJobFactory(this.applicationContext.getAutowireCapableBeanFactory()));
        // 将计划程序设置为等待正在运行的作业完成，然后再关闭
        schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(quartzProperties.isWaitForJobsToCompleteOnShutdown());
        //设置覆盖现有任务
        schedulerFactoryBean.setOverwriteExistingJobs(quartzProperties.isOverwriteExistingJobs());
        //启动延时时间，秒
        schedulerFactoryBean.setStartupDelay((int) quartzProperties.getStartupDelay().getSeconds());
        //是否自动启动
        schedulerFactoryBean.setAutoStartup(quartzProperties.isAutoStartup());
        //quartz参数
        if (!quartzProperties.getProperties().isEmpty()) {
            schedulerFactoryBean.setQuartzProperties(asProperties(quartzProperties.getProperties()));
        }
        //任务调度名称
        schedulerFactoryBean.setSchedulerName(quartzProperties.getSchedulerName());
        //设置上下文
        schedulerFactoryBean.setApplicationContext(applicationContext);
        //设置线程池
        schedulerFactoryBean.setTaskExecutor(taskExecutor());
        //更改SchedulerFactoryBeanCustomizer
        factoryBeanCustomizerList.stream().forEach(customizer -> {
            customizer.customize(schedulerFactoryBean);
        });

        return schedulerFactoryBean;
    }

    /**
     * 转换为properties对象
     */
    private Properties asProperties(Map<String, String> map) {
        Properties properties = new Properties();
        properties.putAll(map);
        return properties;
    }

    @Bean
    public TaskExecutor taskExecutor() {
        SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
        taskExecutor.setThreadNamePrefix("quartz-executor-");
        taskExecutor.setThreadGroupName("quartz");
        taskExecutor.setDaemon(true);
        return taskExecutor;
    }

    @Bean
    public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) {
        return schedulerFactoryBean.getScheduler();
    }

}
